/*
 * =====================================================================================
 *       Filename:  http.c
 *         Author:  MIEN
 *    Description:  解析http数据流
 *           Date:  2016-11-20
 * =====================================================================================
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <errno.h>
#include <unistd.h>
#include <sys/socket.h>
#include "../config.h"
#include "../lib/zipmap.h"
#include "http.h"

/* 读取一行 */
static char* _get_line(http_entity_t *http)
{
    char *t, *start, *tail;
    start = t = http->bp;
    tail = http->buffer + HTTP_BUFFER_SIZE;
    while (t < tail) {
        if (*t == '\0') {
            t++;
            http->bp = t;
            return start;
        }
        if (*t == '\r'){
            *t = '\0';
            t++;
            if (*t == '\n') {
                t++;
            }
            http->bp = t;
            return start;
        }
        t++;
    }
    return NULL;
}

/* 
 * 分析一行数据的 key:value 键值对
 * 例如:
 * Content-Length: 2789
 */
static void _parse_kv(char *b, char *key, int key_size, char *value, int value_size)
{
    int i = 0;
    while (1) {
        key[i] = *b;
        if (*b == ':') {
            key[i] = '\0';
            break;
        }
        if ((*b == '\0') || (i >= key_size - 1)) {
            key[0] = '\0';
            value[0] = '\0';
            return;
        }
        b++;
        i++;
    }
    
    while((*b == ':') || (*b == ' '))
        b++;

    i = 0;
    while(1) {
        value[i] = *b;
        if ((*b == '\0') || (i >= value_size - 1)) {
            value[i] = '\0';
            break;
        }
        b++;
        i++;
    }
}

/* 检查 buffer 防止溢出 */
#define CHECK_HTTP_DATA_ERROR if(buf==NULL){http->status=500;return;}

/**
 * 解析HTTP的请求或响应数据
 * 
 * HTTP 请求的格式为:
 * GET /myqueue HTTP/1.1\r\n
 * Host: www.abc.com\r\n
 * Auth: abc123\r\n
 * Content-Length: 143\r\n
 * \r\n
 * content of body
 *
 * HTTP 响应的第一行格式为:
 * HTTP/1.1 200 ok
 * Host: www.abc.com\r\n
 * Queue: myqueue\r\n
 * Count: 1\r\n
 * Content-Length: 143\r\n
 * \r\n
 * content of body
 */
void http_parse(http_entity_t *http)
{
    /* 如果未读取到任何数据 */
    if (strlen(http->buffer) == 0) {
        return;
    }

    /* 重置bp以便从头解析 */
    http->bp = http->buffer;
 
    /* 读取第一行 */
    char *buf = _get_line(http);
    CHECK_HTTP_DATA_ERROR

    /* 解析第一行 */
    int i, j;

    /* GET, POST, HEAD, DELETE, ... */
    int size = sizeof(http->method) - 1;
    for (i = 0, j = 0; buf[i] != ' ' && j < size; i++, j++) {
        http->method[j] = buf[i];
    }
    http->method[j] = '\0';

    /* 如果是服务端响应 */
    if (!strncmp(http->method, "HTTP/1", 6)) {
        strcpy(http->method, "RESP");
        /* 获取 status */
        while (buf[i] == ' ') {
            i++;
        }
        char *p = buf + i;
        while (buf[i] >= '0' && buf[i] <= '9') {
            i++;
        }
        buf[i] = '\0';
        http->status = atoi(p);
    }
    /* 如果是客户端请求 */
    else {
        /* 获取uri */
        while (buf[i] == ' ') {
            i++;
        }
        size = sizeof(http->uri) - 1;
        j = 0;
        for (; j < size; i++, j++) {
            if (buf[i] == ' ' || buf[i] == '\0')
                break;
            http->uri[j] = buf[i];
        }
        http->uri[j] = '\0';

        /* 从uri中获取队列名称 */
        http->queue[0] = '\0';
        if (strlen(http->uri) > 0) {
            size = sizeof(http->queue) - 1;
            char *p = http->uri;
            j = 0;
            if ((*p) == '/') {
                p++;
            }
            while((*p) != '/' && (*p) != '\0' && j < size) {
                http->queue[j] = (*p);
                p++;
                j++;
            }
            http->queue[j] = '\0';
        }
        if (strlen(http->queue) == 0) {
            strcpy(http->queue, "default");
        }
    }

    /* 分析第二行开始的键值对 */
    while (1) {
        buf = _get_line(http);
        CHECK_HTTP_DATA_ERROR

        if (strlen(buf) == 0)
            break;

        static char key[128];
        static char value[256];

        _parse_kv(buf, key, 128, value, 256);
        http_add_header(http, key, value);
    }

    /* 如果是服务器端响应, 从Header中获取队列名称 */
    if (!strcmp(http->method, "RESP")) {
        char *p = http_get_header(http, "Queue");
        if (p == NULL) {
            strcpy(http->queue, "default");
        } else {
            size = sizeof(http->queue) - 1;
            int len = strlen(p);
            len = len < size ? len : size;
            strncpy(http->queue, p, len);
            http->queue[len] = '\0';
        }
    }

    /* 内容部分 */
    strcpy(http->body, http->bp);
}

/* 从fd读取数据到缓冲区 */
void http_read(http_entity_t *http, int fd)
{
    int n, left;

    char *tail = http->buffer + HTTP_BUFFER_SIZE - 1;
    while (http->bp < tail) {
        left = tail - http->bp;
        n = read(fd, http->bp, left);
        if (n == -1 && errno == EAGAIN) {
            if (strstr(http->buffer, "\r\n\r\n") != NULL) {
                http->step = WRITE;
            }
            return;
        }
        if (n <= 0) {
            http_close(http, fd);
            http->step = IDLE;
            return;
        }
        http->bp += n;
        http->bp[0] = '\0';
    }
    if (http->bp == tail) {
        http->step = WRITE;
    }
}

/* 忽略从fd继续传入的数据 */
void http_discard(http_entity_t *http, int fd)
{
    int n;
    while (1) {
        n = read(fd, http->buffer, 1024);
        if (n <= 0)
        break;
    }
}

/* 输出数据 */
static void _htprintf(http_entity_t *http, int fd, const char *format, ...)
{
    if (http->lwritten > 0) {
        va_list ap;
        va_start(ap, format);
        http->lwritten = vdprintf(fd, format, ap);
        va_end(ap);
    }
}

static char *_http_status_text(int status) {
    int i = 0;
    int len = sizeof(http_status_list) / sizeof(http_status_list[0]);
    for (; i < len; i++) {
        if (http_status_list[i].code == status) {
            return http_status_list[i].text;
        }
    }
    return "Unknown";
}

void http_request(http_entity_t *http, int fd)
{
    _htprintf(http, fd, "%s %s HTTP/1.1\r\n", http->method, http->uri);

    char *key, *value;
    zipReset(http->headers);
    while(zipNext(http->headers, &key, &value)) {
        _htprintf(http, fd, "%s: %s\r\n", key , value);
    }
 
    int contentLength = strlen(http->buffer);

    _htprintf(http, fd, "Content-Length: %d\r\n", contentLength);
    _htprintf(http, fd, "Accept: */*\r\n");
    _htprintf(http, fd, "User-Agent: simq/%s\r\n", SIMQ_VERSION);
    _htprintf(http, fd, "Connection: close\r\n\r\n");

    if (contentLength) {
        _htprintf(http, fd, "%s", http->buffer);
    }
}

void http_response(http_entity_t *http, int fd)
{
    _htprintf(http, fd, "HTTP/1.1 %d %s\r\n", http->status, _http_status_text(http->status));
    _htprintf(http, fd, "Server: simq/%s\r\n", SIMQ_VERSION);
    _htprintf(http, fd, "Content-Type: text/plain\r\n");
    _htprintf(http, fd, "Queue: %s\r\n", http->queue);
    _htprintf(http, fd, "Count: %d\r\n", http->count);

    int contentLength = strlen(http->buffer);
    if (http->status != 200) {
        contentLength = 0;
    }

    _htprintf(http, fd, "Content-Length: %d\r\n", contentLength);
    _htprintf(http, fd, "Connection: close\r\n\r\n");

    if (contentLength) {
        _htprintf(http, fd, "%s", http->buffer);
    }
}

void http_close(http_entity_t *http, int fd)
{
    close(fd);
}

void http_init(http_entity_t *http)
{
    http->method[0] = 0;
    http->uri[0] = 0;
    http->queue[0] = 0;
    http->body[0] = 0;

    zipInit(http->headers, HTTP_BUFFER_SIZE);
    http_reset_buffer(http);

    http->status = 200;
    http->count = 0;
    http->lwritten = 1;
}

char *http_get_header(http_entity_t *http, const char *key)
{
    return zipGet(http->headers, key);
}

void http_add_header(http_entity_t *http, const char *key, const char *value)
{
    zipAdd(http->headers, key, value);
}

/* 获取内容数据 */
char *http_get_body(http_entity_t *http)
{
    return http->body;
}

void http_reset_buffer(http_entity_t *http)
{
    http->buffer[0] = '\0';
    http->bp = http->buffer;
}

int http_append_to_buffer(http_entity_t *http, const char *str)
{
    int slen = strlen(str);
    int blen = strlen(http->buffer);
    if (slen == 0 || 
        slen + blen > HTTP_BUFFER_SIZE - 1) {
        return 0;
    }
    strcpy(http->bp, str);
    http->bp += slen;
    return slen;
}
